
#ifndef __tenacitas_crosswords_concept__board__h__
#define __tenacitas_crosswords_concept__board__h__


/** @file */


/** @todo */

// ==> C++ Headers
#include <cstdint>
#include <vector>
#include <list>
#include <memory>
#include <algorithm>
#include <iterator>
#include <cstdlib>

// ==> 3rds Headers

// ==> Our Headers
#include <tenacitas.translator.concept/rc.h>
#include "word.h"
#include "layouts.h"
#include "layout.h"
#include "coordinates.h"
#include "coordinate.h"

// ==> Namespaces
using namespace std;

// ==> Macro Constants

// ==> Macro Commands


/** ==> namespace of the organization */
namespace tenacitas {
    /** ==> namespace for the project */
    namespace crosswords {
        /** ==> bussiness, concept, displaying, interacting, transmitting,
            storing */ 
        namespace concept {

            // ==> Typedefs 

            // ==> Pre-Declarations

            // ==> Attributes 

            // ==> Functions

            // ==> Classes

            /** 
               @tparam supplier must define @p text and @p letter

               @tparam supplier::text is the type of string to be
               used: @p std::string, or @p std::wstring, or @p std::u16string, or
               @p std::u32string. 

               @nosubgrouping 
            */
            template <typename supplier>
            class board {

            public:

                // ==> board - Public Internal Types

                /** */
                typedef typename supplier::text text;

                /** */
                typedef typename text::value_type letter;

                /** */
                typedef concept::layouts<supplier> layouts;

                /** */
                typedef typename layouts::index layouts_index;

                /** */
                typedef typename layouts::layout layout;

                /** */
                typedef typename layout::orientation orientation;

                /** */
                typedef concept::coordinates<supplier> coordinates;


                /** */
                typedef typename coordinates::coordinate coordinate;

                /** */
                typedef concept::word<supplier> word;

                /** */
                typedef typename word::category category;

                /** */
                enum constants : constant_id { error_adding_word = 1,
                        not_enough_words = 2,
                        unable_to_create = 3,
                        could_not_create_matrix = 4,
                        unable_to_fit = 5,
                        word_didnt_fit = 6
                };

                // ==> board - Friends

                /** Class allowed to access private members of @p board, in
                    order to perform tests */
                friend class board_tester;

                // ==> board - Public Constructors

                /** */
                inline board();
                
                 /** */
                inline board(const board &);

                /** */
                board(board &&) = delete;

                /** Destructor */
                inline ~board();

                // ==> board - Public Accessors

                /** */
                inline rc_t add(const word &) noexcept;

                /** */
                inline layouts_index get_size() noexcept;

                // ==> board - Public Helpers

                // ==> board - Public Processors

                /** There must be at least two words added */
                rc_t create() noexcept;

                // ==> board - Public Operators

                /** */
                inline board & operator=(const board &) noexcept;

                /** */
                board & operator=(board &&) = delete;

                /** not allowed */
                bool operator ==(const board &) = delete;

                /** not allowed */
                bool operator !=(const board &) = delete;

                /** not allowed */
                void* operator new (std::size_t size)
                    throw (std::bad_alloc) = delete; 

                /** not allowed */
                void operator delete (void *p_class_id) = delete;

                /** not allowed */
                void *operator new[] (size_t) = delete;

                /** not allowed */
                void operator delete[] (void *) = delete;

                // ==> board - Public Attributes

                /** */
                static const class_id id = 14296292427543ULL;

            private:

                // ==> board - Private Internal Types

                /** */
                typedef typename coordinates::index coordinates_index;

                /** */
                typedef typename layout::layout_id layout_id;

                /** 
                    @nosubgrouping 
                */
                class implementation {

                public:

                    // ==> implementation - Public Constructors

                    /** */
                    inline implementation() noexcept;

                    /** */
                    implementation(const implementation &) = delete;

                    /** */
                    implementation(implementation &&) = delete;

                    /** Destructor */
                    inline ~implementation();

                    // ==> implementation - Public Accessors

                    /** */
                    inline rc_t add(const word &) noexcept;

                    /** */
                    inline layouts_index get_size() noexcept;

                    // ==> implementation - Public Helpers

                    /** */
                    template <typename t_out>
                    void print(t_out & p_out);                    

                    // ==> implementation - Public Processors

                    /** */
                    rc_t create();
                
                    // ==> implementation - Public Operators

                    /** */
                    implementation & operator=(const implementation &)=delete;

                    /** */
                    implementation & operator=(implementation &&)=delete;

                    /** not allowed */
                    void *operator new[] (size_t) = delete;

                    /** not allowed */
                    void operator delete[] (void *) = delete;

                    /** not allowed */
                    bool operator ==(const implementation &)=delete;

                    /** not allowed */
                    bool operator !=(const implementation &)=delete;

                private:

                    // ==> implementation - Private Processors

                    /** */
                    rc_t position_vertical(iterator p_to_position,
                                           iterator p_reference);

                    /** */
                    rc_t position_horizontal(iterator p_to_position,
                                             iterator p_reference);
                    
                    
                    // ==> implementation - Private Attributes

                    /** */
                    layouts m_layouts;

                };

                /** */
                typedef std::shared_ptr<implementation> ptr;

                // ==> board - Private Attributes

                /** */
                ptr m_implementation;

            };
        }
    }
}
// =============================================================================
// I M P L E M E N T A T I O N
// =============================================================================

using namespace tenacitas::crosswords::concept;

// board #######################################################################

// -----------------------------------------------------------------------------
template <typename supplier>
const class_id
board<supplier>::id;

// -----------------------------------------------------------------------------
template <typename supplier>
board<supplier>::
board() 
    : m_implementation(new implementation()) {}

// -----------------------------------------------------------------------------
template <typename supplier>
board<supplier>::
board(const board & p_board)
    : m_implementation(p_board.m_implementation) {}
    
// -----------------------------------------------------------------------------
template <typename supplier>
board<supplier>::
~board() {}

// -----------------------------------------------------------------------------
template <typename supplier>
rc_t
board<supplier>::
add(const word & p_word) noexcept {return m_implementation->add(p_word);}

// -----------------------------------------------------------------------------
template <typename supplier>
typename board<supplier>::index
board<supplier>::
get_size() noexcept {return m_implementation->get_size();}

// -----------------------------------------------------------------------------
template <typename supplier>
rc_t
board<supplier>::
create() noexcept {
    return m_implementation->create();
}

// -----------------------------------------------------------------------------
template <typename supplier>
board<supplier> &
board<supplier>::
operator=(const board & p_board) noexcept {
    if (this != & p_board) {
        m_implementation = p_board.m_implementation;
    }
    return *this;
}

// -----------------------------------------------------------------------------
template <typename supplier>
template <typename t_out>
void
board<supplier>::
print(t_out & p_out) {m_implementation->print(p_out);}


// board<supplier>::implementation #############################################


// -----------------------------------------------------------------------------
template <typename supplier>
board<supplier>::implementation::
implementation() noexcept
    : m_layouts(){}

// -----------------------------------------------------------------------------
template <typename supplier>
board<supplier>::implementation::
~implementation() {
}

// -----------------------------------------------------------------------------
template <typename supplier>
rc_t
board<supplier>::implementation::
add(const word & p_word) noexcept {
    try {
        m_layouts.add(p_word);
    } catch (std::exception &) {
        return rc_t(board<supplier>::id, board<supplier>::error_adding_word);
    }
    return rc_t();
}


// -----------------------------------------------------------------------------
template <typename supplier>
rc_t
board<supplier>::implementation::
create() {
    m_layouts.sort();
    return m_layouts.position();
}




// -----------------------------------------------------------------------------
template <typename supplier>
rc_t
board<supplier>::implementation::
position_vertical(iterator p_target,
                  iterator p_reference) {

    // // reference data
    // const coordinate & _ref_coord_begin = p_reference->get_coordinate();

    // orientation _ref_ori = p_reference->get_orientation();

    // const typename word::text & _ref_word = p_reference->get_word().get_value();

    // index _ref_word_size = static_cast<index>(_ref_word.size());

    // coordinate _ref_coord_end (_ref_coord_begin.get_line(),
    //                            _ref_coord_begin.get_column() + _ref_word_size);

    
    // // target data
    // coordinate _target_coord_begin;
    // coordinate _target_coord_end;

    // orientation _target_ori = orientation::ver;

    // const typename word::text & _target_word = p_target->get_word().get_value();

    // index _target_word_size = static_cast<index>(_target_word.size());
    
    // coordinate _match_coord;

    // // tries to find a letter in the reference that matches a letter in the
    // // target layout
    // for (index _r = 0; _r < _ref_word_size; ++_r) {
    //     for (index _t = 0; _t < _target_word_size; ++_t) {
    //         if (_ref_word[_r] == _target_word[_t]) {

    //             // a match!
    //             _target_coord_begin.set_line(_ref_coord_begin.get_line() - _t);
    //             _target_coord_begin.set_column(_ref_coord_begin.get_column() +
    //                                            _r); 

    //             _target_coord_end.set_line(_ref_coord_begin.get_line() +
    //                                        (_target_word_size - _t) - 1);
    //             _target_coord_end.set_column(_target_coord_begin.get_column());

    //             _match_coord.set_line (_ref_coord_begin.get_line());
    //             _match_coord.set_column(_target_coord_begin.get_column());

    //             // checking surroundings
    //             if ( (may_occupy(_target_coord_begin,
    //                              _target_coord_end,
    //                              _match_coord,
    //                              _target_ori)) ) {
    //                 p_target->set_orientation(_target_ori);
    //                 p_target->set_coordinate(_target_coord_begin);

    //                 add_non_free_coordinates(p_target);
                    
    //                 return rc_t();
    //             }
    //         }
    //     }
    // }
    
    
    // const typename word::text & _ref_text = p_reference->get_word().get_value();
    
    // index _ref_size = static_cast<index>(_ref_text.size());
    
    // index _ref_ci = p_reference->get_coordinate().get_column();
    // index _ref_cf = p_reference->get_coordinate().get_column() +
    //     _ref_size;

    // index _ref_l = p_reference->get_coordinate().get_line();

    // const typename word::text & _to_text = p_to_position->get_word().get_value();

    // index _to_size = static_cast<index>(_to_text.size());

    // index _last = m_size - 1;

    // index _to_li;
    // index _to_lf;
    
    // bool _col_is_first = false;
    // bool _col_is_last = false;

    // bool _yes = true;

    // bool _before_is_free = true;
    // bool _after_is_free = true;
    
    // for (index _col = _ref_ci; _col < _ref_cf; ++_col) {
    //     for (index _i = 0; _i < _to_size; ++_i) {
    //         if (m_matrix[_ref_l][_col] == _to_text[_i]) {

    //             _col_is_first = (_col == 0);
    //             _col_is_last = (_col == _last);
                
    //             _to_li = _ref_l - _i;
    //             _to_lf = _ref_l + (_to_size - _i) - 1;

    //             _before_is_free = true;
    //             if (_to_li > 0) {
    //                 _before_is_free = (m_matrix[_to_li - 1][_col] == ' ');
    //             }
                
    //             _after_is_free = true;
    //             if (_to_lf < _last ){
    //                 _after_is_free = (m_matrix[_to_lf + 1][_col] == ' ');
    //             }


    //             if ( (_to_li >= 0) &&
    //                  (_to_lf <= _last) && 
    //                  (_before_is_free) && 
    //                  (_after_is_free)
    //                 ) {
                    
    //                 _yes = true;
                
    //                 for (index _j = _to_li; _j <= _to_lf; ++_j) {
    //                     if (_j != _ref_l) {
    //                         if ( (!_col_is_first) && 
    //                              (m_matrix[_j][_col - 1] != ' ') ) {
    //                             _yes = false;
    //                             break;
    //                         }

    //                         if ( (!_col_is_last) && 
    //                              (m_matrix[_j][_col + 1] != ' ') ) {
    //                             _yes = false;
    //                             break;
    //                         }

    //                         if ( (m_matrix[_j][_col] != ' ') &&
    //                              (m_matrix[_j][_col] != _to_text[_j] ) ) {
    //                             _yes = false;
    //                             break;
    //                         }
    //                     }
    //                 }
    //                 if (_yes) {
    //                     p_to_position->set_orientation(orientation::ver);
    //                     p_to_position->set_coordinate(coordinate(_to_li,
    //                                                                     _col));
    //                     return rc_t();
    //                 }
    //             }
    //         }
    //     }
    // }
                
    return rc_t(board::id, 
                board::constants::word_didnt_fit);
}

// -----------------------------------------------------------------------------
template <typename supplier>
rc_t
board<supplier>::implementation::
position_horizontal(iterator p_to_position,
                    iterator p_reference) {

    // const typename word::text & _ref_text = p_reference->get_word().get_value();
    
    // index _ref_size = static_cast<index>(_ref_text.size());
    
    // index _ref_l_i = p_reference->get_coordinate().get_line();
    // index _ref_l_f = p_reference->get_coordinate().get_line() +
    //     _ref_size;

    // index _ref_c = p_reference->get_coordinate().get_column();

    // const typename word::text & _to_text = p_to_position->get_word().get_value();

    // index _to_size = static_cast<index>(_to_text.size());

    // index _last = m_size - 1;

    // index _to_c_i;
    // index _to_c_f;
    
    // bool _line_is_first = false;
    // bool _line_is_last = false;

    // bool _yes = true;

    // bool _before_is_free = true;
    // bool _after_is_free = true;
    
    // for (index _line = _ref_l_i; _line < _ref_l_f; ++_line) {
    //     for (index _i = 0; _i < _to_size; ++_i) {
    //         if (m_matrix[_line][_ref_c] == _to_text[_i]) {

    //             _line_is_first = (_line == 0);
    //             _line_is_last = (_line == _last);
                
    //             _to_c_i = _ref_c - _i;
    //             _to_c_f = _ref_c + (_to_size - _i) - 1;

    //             _before_is_free = true;
    //             if (_to_c_i > 0) {
    //                 _before_is_free = (m_matrix[_line][_to_c_i - 1] == ' ');
    //             }
                
    //             _after_is_free = true;
    //             if (_to_c_f < _last ){
    //                 _after_is_free = (m_matrix[_line][_to_c_f + 1] == ' ');
    //             }

    //             if ( (_to_c_i >= 0) &&
    //                  (_to_c_f <= _last) &&
    //                  (_before_is_free) && 
    //                  (_after_is_free)
    //                 ) {

    //                 _yes = true;
                
    //                 for (index _j = _to_c_i; _j <= _to_c_f; ++_j) {
    //                     if (_j != _ref_c) {
                        
    //                         if ( (!_line_is_first) && 
    //                              (m_matrix[_line - 1][_j] != ' ') ) {
    //                             _yes = false;
    //                             break;
    //                         }

    //                         if ( (!_line_is_last) && 
    //                              (m_matrix[_line + 1][_j] != ' ') ) {
    //                             _yes = false;
    //                             break;
    //                         }

    //                         if ( (m_matrix[_line][_j] != ' ') &&
    //                              (m_matrix[_line][_j] != _to_text[_j] ) ) {
    //                             _yes = false;
    //                             break;
    //                         }
    //                     }
    //                 }
    //                 if (_yes) {
    //                     p_to_position->set_orientation(orientation::hor);
    //                     p_to_position->set_coordinate(coordinate(_line,
    //                                                                     _to_c_i));
    //                     return rc_t();
    //                 }
    //             }
    //         }
    //     }
    // }
                
    return rc_t(board::id, 
                board::constants::word_didnt_fit);
}


// -----------------------------------------------------------------------------
template <typename supplier>
typename board<supplier>::index
board<supplier>::implementation::
get_size() noexcept {return static_cast<index>(m_layouts.size());}



#endif
